using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace IppServer.Host.Tools
{
    /// <summary>
    /// IPP 客户端诊断工具 - 独立程序
    /// 在客户端机器上运行此工具以诊断 IPP 打印问题
    /// </summary>
    public static class IppClientDiagnosticTool
    {
        public static void RunDiagnostic(string printerUri = null)
        {
            var report = new StringBuilder();
            report.AppendLine("=".PadRight(80, '='));
            report.AppendLine("IPP 打印机客户端诊断工具");
            report.AppendLine("=".PadRight(80, '='));
            report.AppendLine($"诊断时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
            report.AppendLine();

            if (string.IsNullOrEmpty(printerUri))
            {
                // 如果没有提供 URI，尝试从用户输入获取
                var inputForm = new Form
                {
                    Text = "输入打印机 URI",
                    Width = 500,
                    Height = 150,
                    StartPosition = FormStartPosition.CenterScreen,
                    FormBorderStyle = FormBorderStyle.FixedDialog,
                    MaximizeBox = false
                };

                var label = new Label
                {
                    Text = "请输入 IPP 打印机 URI:",
                    Location = new System.Drawing.Point(10, 15),
                    Width = 460
                };

                var textBox = new TextBox
                {
                    Text = "ipp://192.168.1.4:8631/printers/hp1018",
                    Location = new System.Drawing.Point(10, 40),
                    Width = 460
                };

                var okButton = new Button
                {
                    Text = "确定",
                    DialogResult = DialogResult.OK,
                    Location = new System.Drawing.Point(300, 75),
                    Width = 80
                };

                var cancelButton = new Button
                {
                    Text = "取消",
                    DialogResult = DialogResult.Cancel,
                    Location = new System.Drawing.Point(390, 75),
                    Width = 80
                };

                inputForm.Controls.AddRange(new Control[] { label, textBox, okButton, cancelButton });
                inputForm.AcceptButton = okButton;
                inputForm.CancelButton = cancelButton;

                if (inputForm.ShowDialog() == DialogResult.OK)
                {
                    printerUri = textBox.Text.Trim();
                }
                else
                {
                    return;
                }
            }

            report.AppendLine($"打印机 URI: {printerUri}");
            report.AppendLine();

            // 将 ipp:// 转换为 http://（因为 .NET Framework 不支持 ipp:// 协议）
            var httpUri = printerUri;
            if (httpUri.StartsWith("ipp://", StringComparison.OrdinalIgnoreCase))
            {
                httpUri = "http://" + httpUri.Substring(6);
                report.AppendLine($"转换为 HTTP URI: {httpUri}");
                report.AppendLine();
            }

            // 异步运行诊断
            var task = DiagnosePrinterAsync(httpUri, report);
            task.Wait();

            // 显示诊断报告
            var resultForm = new Form
            {
                Text = "IPP 打印机诊断报告",
                Width = 900,
                Height = 700,
                StartPosition = FormStartPosition.CenterScreen
            };

            var resultTextBox = new TextBox
            {
                Multiline = true,
                ReadOnly = true,
                ScrollBars = ScrollBars.Both,
                Dock = DockStyle.Fill,
                Font = new System.Drawing.Font("Consolas", 9),
                Text = report.ToString()
            };

            var saveButton = new Button
            {
                Text = "保存报告",
                Dock = DockStyle.Bottom,
                Height = 30
            };

            saveButton.Click += (s, e) =>
            {
                var saveDialog = new SaveFileDialog
                {
                    Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*",
                    FileName = $"IPP诊断报告_{DateTime.Now:yyyyMMdd_HHmmss}.txt"
                };

                if (saveDialog.ShowDialog() == DialogResult.OK)
                {
                    File.WriteAllText(saveDialog.FileName, report.ToString(), Encoding.UTF8);
                    MessageBox.Show("报告已保存", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            };

            resultForm.Controls.Add(resultTextBox);
            resultForm.Controls.Add(saveButton);
            resultForm.ShowDialog();
        }

        private static async Task DiagnosePrinterAsync(string printerUri, StringBuilder report)
        {
            try
            {
                // 1. 检查网络连接
                report.AppendLine("=== 1. 网络连接检查 ===");
                var uri = new Uri(printerUri);
                var host = uri.Host;
                var port = uri.Port;

                try
                {
                    // 测试根路径（可能返回 404，但说明服务器在运行）
                    var testUrl = $"http://{host}:{port}";
                    var request = (HttpWebRequest)WebRequest.Create(testUrl);
                    request.Timeout = 5000;
                    request.Method = "GET";
                    try
                    {
                        using (var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(
                            request.BeginGetResponse, request.EndGetResponse, null))
                        {
                            report.AppendLine($"✓ HTTP 连接成功: {testUrl}");
                            report.AppendLine($"  状态码: {response.StatusCode} ({response.StatusDescription})");
                        }
                    }
                    catch (WebException wex)
                    {
                        // 404 或其他错误说明服务器在运行，只是路径不存在
                        if (wex.Response != null)
                        {
                            var httpResponse = (HttpWebResponse)wex.Response;
                            report.AppendLine($"✓ 服务器响应: {testUrl}");
                            report.AppendLine($"  状态码: {httpResponse.StatusCode} ({httpResponse.StatusDescription})");
                            report.AppendLine($"  (404 是正常的，说明服务器在运行)");
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ HTTP 连接失败: {ex.Message}");
                    report.AppendLine($"  异常类型: {ex.GetType().Name}");
                }
                report.AppendLine();

                // 2. 发送 Get-Printer-Attributes 请求
                report.AppendLine("=== 2. Get-Printer-Attributes 请求测试 ===");
                var ippRequest = BuildGetPrinterAttributesRequest(printerUri);
                var requestBytes = ippRequest;

                try
                {
                    var request = (HttpWebRequest)WebRequest.Create(printerUri);
                    request.Timeout = 10000;
                    request.Method = "POST";
                    request.ContentType = "application/ipp";
                    request.ContentLength = requestBytes.Length;

                    report.AppendLine($"发送 IPP 请求到: {printerUri}");
                    report.AppendLine($"请求大小: {requestBytes.Length} 字节");
                    report.AppendLine($"请求前64字节: {BitConverter.ToString(requestBytes, 0, Math.Min(64, requestBytes.Length)).Replace("-", " ")}");

                    using (var requestStream = await Task.Factory.FromAsync<Stream>(
                        request.BeginGetRequestStream, request.EndGetRequestStream, null))
                    {
                        await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);
                    }

                    using (var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(
                        request.BeginGetResponse, request.EndGetResponse, null))
                    {
                        report.AppendLine($"✓ 收到响应");
                        report.AppendLine($"  状态码: {response.StatusCode} ({response.StatusDescription})");
                        report.AppendLine($"  Content-Type: {response.ContentType}");
                        report.AppendLine($"  Content-Length: {response.ContentLength}");

                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            using (var responseStream = response.GetResponseStream())
                            using (var memoryStream = new MemoryStream())
                            {
                                await responseStream.CopyToAsync(memoryStream);
                                var responseBytes = memoryStream.ToArray();
                                report.AppendLine($"  响应大小: {responseBytes.Length} 字节");
                                report.AppendLine($"  响应前64字节: {BitConverter.ToString(responseBytes, 0, Math.Min(64, responseBytes.Length)).Replace("-", " ")}");

                                // 解析响应
                                ParseIppResponse(responseBytes, report);
                            }
                        }
                        else
                        {
                            report.AppendLine($"✗ 请求失败");
                            using (var responseStream = response.GetResponseStream())
                            using (var reader = new StreamReader(responseStream))
                            {
                                var errorContent = await reader.ReadToEndAsync();
                                report.AppendLine($"  错误内容: {errorContent}");
                            }
                        }
                    }
                }
                catch (WebException ex)
                {
                    report.AppendLine($"✗ 网络异常: {ex.Message}");
                    if (ex.Response != null)
                    {
                        using (var responseStream = ex.Response.GetResponseStream())
                        using (var reader = new StreamReader(responseStream))
                        {
                            var errorContent = await reader.ReadToEndAsync();
                            report.AppendLine($"  错误响应: {errorContent}");
                        }
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 请求异常: {ex.Message}");
                    report.AppendLine($"  异常类型: {ex.GetType().Name}");
                    report.AppendLine($"  堆栈跟踪: {ex.StackTrace}");
                }
                report.AppendLine();

                // 3. 检查 Windows 打印后台处理程序
                report.AppendLine("=== 3. Windows 打印后台处理程序检查 ===");
                try
                {
                    var spoolerService = ServiceController.GetServices()
                        .FirstOrDefault(s => s.ServiceName == "Spooler");
                    if (spoolerService != null)
                    {
                        report.AppendLine($"✓ Spooler 服务已找到");
                        report.AppendLine($"  服务名称: {spoolerService.ServiceName}");
                        report.AppendLine($"  显示名称: {spoolerService.DisplayName}");
                        report.AppendLine($"  状态: {spoolerService.Status}");
                        report.AppendLine($"  启动类型: {spoolerService.StartType}");
                    }
                    else
                    {
                        report.AppendLine($"✗ 未找到 Spooler 服务");
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 检查失败: {ex.Message}");
                }
                report.AppendLine();

                // 4. 检查已安装的打印机
                report.AppendLine("=== 4. 已安装的打印机 ===");
                try
                {
                    var printers = System.Drawing.Printing.PrinterSettings.InstalledPrinters;
                    report.AppendLine($"找到 {printers.Count} 个已安装的打印机:");
                    foreach (string printerName in printers)
                    {
                        try
                        {
                            var settings = new System.Drawing.Printing.PrinterSettings { PrinterName = printerName };
                            report.AppendLine($"  - {printerName}");
                            report.AppendLine($"    默认页面: {settings.DefaultPageSettings.PaperSize.PaperName}");
                            report.AppendLine($"    是否有效: {settings.IsValid}");
                        }
                        catch
                        {
                            report.AppendLine($"  - {printerName} (无法获取详细信息)");
                        }
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 检查失败: {ex.Message}");
                }
                report.AppendLine();

                // 5. 检查 Windows 事件日志
                report.AppendLine("=== 5. Windows 事件日志检查 ===");
                try
                {
                    CheckEventLogs(report);
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ 检查事件日志失败: {ex.Message}");
                }
                report.AppendLine();

                // 6. 尝试发送 Validate-Job 请求
                report.AppendLine("=== 6. Validate-Job 请求测试 ===");
                try
                {
                    var validateRequest = BuildValidateJobRequest(printerUri);
                    var validateBytes = validateRequest;

                    var request = (HttpWebRequest)WebRequest.Create(printerUri);
                    request.Timeout = 10000;
                    request.Method = "POST";
                    request.ContentType = "application/ipp";
                    request.ContentLength = validateBytes.Length;

                    report.AppendLine($"发送 Validate-Job 请求...");

                    using (var requestStream = await Task.Factory.FromAsync<Stream>(
                        request.BeginGetRequestStream, request.EndGetRequestStream, null))
                    {
                        await requestStream.WriteAsync(validateBytes, 0, validateBytes.Length);
                    }

                    using (var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(
                        request.BeginGetResponse, request.EndGetResponse, null))
                    {
                        report.AppendLine($"✓ 收到响应");
                        report.AppendLine($"  状态码: {response.StatusCode} ({response.StatusDescription})");

                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            using (var responseStream = response.GetResponseStream())
                            using (var memoryStream = new MemoryStream())
                            {
                                await responseStream.CopyToAsync(memoryStream);
                                var responseBytes = memoryStream.ToArray();
                                ParseIppResponse(responseBytes, report);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    report.AppendLine($"✗ Validate-Job 请求失败: {ex.Message}");
                }
                report.AppendLine();

            }
            catch (Exception ex)
            {
                report.AppendLine($"✗ 诊断过程发生异常: {ex.Message}");
                report.AppendLine($"异常类型: {ex.GetType().Name}");
                report.AppendLine($"堆栈跟踪: {ex.StackTrace}");
            }

            report.AppendLine("=".PadRight(80, '='));
            report.AppendLine("诊断完成");
            report.AppendLine("=".PadRight(80, '='));
        }

        private static byte[] BuildGetPrinterAttributesRequest(string printerUri)
        {
            using (var stream = new MemoryStream())
            {
                // IPP 版本 2.0
                stream.WriteByte(0x02);
                stream.WriteByte(0x00);

                // 操作代码: Get-Printer-Attributes (0x000B)
                stream.WriteByte(0x00);
                stream.WriteByte(0x0B);

                // 请求 ID
                var requestId = 1;
                stream.WriteByte((byte)((requestId >> 24) & 0xFF));
                stream.WriteByte((byte)((requestId >> 16) & 0xFF));
                stream.WriteByte((byte)((requestId >> 8) & 0xFF));
                stream.WriteByte((byte)(requestId & 0xFF));

                // 操作属性组 (0x01)
                stream.WriteByte(0x01);

                // attributes-charset
                WriteStringAttribute(stream, 0x47, "attributes-charset", "utf-8");

                // attributes-natural-language
                WriteStringAttribute(stream, 0x48, "attributes-natural-language", "en-us");

                // printer-uri
                WriteStringAttribute(stream, 0x45, "printer-uri", printerUri);

                // 结束标记 (0x03)
                stream.WriteByte(0x03);

                return stream.ToArray();
            }
        }

        private static byte[] BuildValidateJobRequest(string printerUri)
        {
            using (var stream = new MemoryStream())
            {
                // IPP 版本 2.0
                stream.WriteByte(0x02);
                stream.WriteByte(0x00);

                // 操作代码: Validate-Job (0x0004)
                stream.WriteByte(0x00);
                stream.WriteByte(0x04);

                // 请求 ID
                var requestId = 2;
                stream.WriteByte((byte)((requestId >> 24) & 0xFF));
                stream.WriteByte((byte)((requestId >> 16) & 0xFF));
                stream.WriteByte((byte)((requestId >> 8) & 0xFF));
                stream.WriteByte((byte)(requestId & 0xFF));

                // 操作属性组 (0x01)
                stream.WriteByte(0x01);

                // attributes-charset
                WriteStringAttribute(stream, 0x47, "attributes-charset", "utf-8");

                // attributes-natural-language
                WriteStringAttribute(stream, 0x48, "attributes-natural-language", "en-us");

                // printer-uri
                WriteStringAttribute(stream, 0x45, "printer-uri", printerUri);

                // document-format
                WriteStringAttribute(stream, 0x49, "document-format", "application/pdf");

                // 结束标记 (0x03)
                stream.WriteByte(0x03);

                return stream.ToArray();
            }
        }

        private static void WriteStringAttribute(Stream stream, byte tag, string name, string value)
        {
            var nameBytes = Encoding.ASCII.GetBytes(name);
            var valueBytes = Encoding.UTF8.GetBytes(value);

            stream.WriteByte(tag);
            stream.WriteByte((byte)(nameBytes.Length >> 8));
            stream.WriteByte((byte)(nameBytes.Length & 0xFF));
            stream.Write(nameBytes, 0, nameBytes.Length);
            stream.WriteByte((byte)(valueBytes.Length >> 8));
            stream.WriteByte((byte)(valueBytes.Length & 0xFF));
            stream.Write(valueBytes, 0, valueBytes.Length);
        }

        private static void ParseIppResponse(byte[] response, StringBuilder report)
        {
            if (response.Length < 8)
            {
                report.AppendLine("✗ 响应太短，无法解析");
                return;
            }

            try
            {
                var versionMajor = response[0];
                var versionMinor = response[1];
                var statusCode = (response[2] << 8) | response[3];
                var requestId = (response[4] << 24) | (response[5] << 16) | (response[6] << 8) | response[7];

                report.AppendLine($"  IPP 版本: {versionMajor}.{versionMinor}");
                report.AppendLine($"  状态码: 0x{statusCode:X4} ({GetStatusCodeName(statusCode)})");
                report.AppendLine($"  请求 ID: {requestId}");

                // 简单的属性解析（仅用于诊断）
                var offset = 8;
                var attributeCount = 0;
                while (offset < response.Length)
                {
                    var tag = response[offset];
                    if (tag == 0x03) // 结束标记
                        break;

                    offset++;
                    if (offset >= response.Length) break;

                    var nameLength = (response[offset] << 8) | response[offset + 1];
                    offset += 2;
                    if (offset + nameLength > response.Length) break;

                    var name = nameLength > 0 ? Encoding.ASCII.GetString(response, offset, nameLength) : "";
                    offset += nameLength;
                    if (offset + 2 > response.Length) break;

                    var valueLength = (response[offset] << 8) | response[offset + 1];
                    offset += 2;
                    if (offset + valueLength > response.Length) break;

                    if (nameLength > 0 && valueLength > 0 && valueLength < 256)
                    {
                        var value = Encoding.UTF8.GetString(response, offset, valueLength);
                        if (name == "printer-name" || name == "printer-uri-supported" ||
                            name == "operations-supported" || name == "printer-is-accepting-jobs" ||
                            name == "document-format-supported" || name == "document-format-preferred" ||
                            name == "printer-state" || name == "printer-state-reasons")
                        {
                            report.AppendLine($"  {name} = {value}");
                            attributeCount++;
                        }
                    }
                    offset += valueLength;
                }
                report.AppendLine($"  解析了 {attributeCount} 个关键属性");
            }
            catch (Exception ex)
            {
                report.AppendLine($"✗ 解析响应时出错: {ex.Message}");
            }
        }

        private static void CheckEventLogs(StringBuilder report)
        {
            try
            {
                var logNames = new[] { "Application", "System" };
                var keywords = new[] { "print", "printer", "ipp", "spooler", "spool" };

                foreach (var logName in logNames)
                {
                    try
                    {
                        var eventLog = new EventLog(logName);
                        var entries = eventLog.Entries.Cast<EventLogEntry>()
                            .Where(e => e.TimeGenerated > DateTime.Now.AddHours(-1))
                            .Where(e => keywords.Any(k => e.Message.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0 ||
                                                          e.Source.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0))
                            .OrderByDescending(e => e.TimeGenerated)
                            .Take(10)
                            .ToList();

                        if (entries.Count > 0)
                        {
                            report.AppendLine($"在 {logName} 日志中找到 {entries.Count} 个相关事件:");
                            foreach (var entry in entries)
                            {
                                report.AppendLine($"  [{entry.TimeGenerated:yyyy-MM-dd HH:mm:ss}] {entry.EntryType}: {entry.Source}");
                                report.AppendLine($"    {entry.Message.Substring(0, Math.Min(200, entry.Message.Length))}...");
                            }
                        }
                        else
                        {
                            report.AppendLine($"在 {logName} 日志中未找到相关事件");
                        }
                    }
                    catch (Exception ex)
                    {
                        report.AppendLine($"无法读取 {logName} 日志: {ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                report.AppendLine($"检查事件日志失败: {ex.Message}");
            }
        }

        private static string GetStatusCodeName(int statusCode)
        {
            switch (statusCode)
            {
                case 0x0000: return "successful-ok";
                case 0x0001: return "successful-ok-ignored-or-substituted-attributes";
                case 0x0400: return "client-error-bad-request";
                case 0x0401: return "client-error-forbidden";
                case 0x0402: return "client-error-not-authenticated";
                case 0x0403: return "client-error-not-authorized";
                case 0x0404: return "client-error-not-possible";
                case 0x0405: return "client-error-timeout";
                case 0x0406: return "client-error-not-found";
                case 0x0409: return "client-error-gone";
                case 0x040A: return "client-error-request-entity-too-large";
                case 0x040B: return "client-error-request-value-too-long";
                case 0x040C: return "client-error-document-format-not-supported";
                case 0x040D: return "client-error-attributes-or-values-not-supported";
                case 0x040E: return "client-error-uri-scheme-not-supported";
                case 0x040F: return "client-error-charset-not-supported";
                case 0x0410: return "client-error-compression-not-supported";
                case 0x0411: return "client-error-compression-error";
                case 0x0412: return "client-error-document-format-error";
                case 0x0413: return "client-error-document-access-error";
                default: return $"unknown-{statusCode:X4}";
            }
        }
    }
}

